#include "stdio.h"
#include <stdlib.h>
#include <string.h>

#include "digit_property.h"
#include "string_stack.h"
#define DEBUG_PRINT(fmt, ...)                                                  \
  do {                                                                         \
    if (debug_required)                                                        \
      fprintf(stderr, fmt, ##__VA_ARGS__);                                     \
  } while (0)
bool debug_required;

int main(int argc, char **argv) {

  char *debug_flag = getenv("DEBUG");
  bool debug_required = debug_flag && *debug_flag == '1';
  int front, rear, res;
  if (argc != 2 || !strcmp("help", argv[1]) || !strcmp("--help", argv[1])) {
    fprintf(stderr,
            "a simple program to generate polish expression.\nusage:\n");
    fprintf(stderr, "%s <expr>\n", argv[0]);
    fprintf(stderr,
            "Note: only single letter/digits can be used now as opends\n");
    return argc == 1;
  }
  const long target_size = strlen(argv[1]) + 1;
  DEBUG_PRINT("processing %s with length %d...\n----\n", argv[1],
              (int)target_size - 1);
  char *stack = calloc(target_size, sizeof(char));
  char *res_buf = calloc(target_size, sizeof(char));
  char *calc_buf = calloc(target_size, sizeof(char));
  if (!stack || !res_buf) {
    fprintf(stderr, "malloc error\n");
    return 2;
  }
  for (char *p = argv[1]; *p; p++) {

    DEBUG_PRINT("\n---[%3ld]--- \nwith stack %*s and cumulated output %*s, %c "
                "at %ld is to be read\n",
                p - argv[1], (int)target_size, stack, (int)target_size, res_buf,
                *p, p - argv[1]);
    if (is_opend(*p)) {
      push(res_buf, *p);
      continue;
    }
    // do braces
    switch (*p) {
    case ' ':
    case '\t':
    case '\n':
      if (debug_required)
        fprintf(stderr, "omitting %c (%d)\n", *p, *p);
      continue;

    case '(':
      push(stack, *p);
      continue;
    case ')':
      for (;;) {
        char peek_result_for_right_brace;
        // int stackSize = strlen(stack);
        if (!pop(stack, &peek_result_for_right_brace)) {
          fprintf(stderr, "error:\')\' at location %ld not matched\n",
                  p - argv[1]);
          return -1;
        }
        if (peek_result_for_right_brace == '(') {
          DEBUG_PRINT("left brace met. breaking\n");
          break;
        }
        // pop(stack, &peek_result_for_right_brace);
        push(res_buf, peek_result_for_right_brace);
      }
      continue;
    default:
      break;
    }
    if (is_calc(*p)) {

      int this_priority = get_priority(*p);
      DEBUG_PRINT("processing operator %c with priority %d [%s] \t...\n", *p,
                  this_priority, priority[this_priority]);
      // do calcs
      for (;;) {
        char pop_result = 0;
        if (!peek(stack, &pop_result) || pop_result == '(') {
          DEBUG_PRINT("all operators reallocated before %c\n", pop_result);
          DEBUG_PRINT("\tresult buf   [%ld]: %*s\n", strlen(res_buf),
                      (int)target_size, res_buf);
          DEBUG_PRINT("\toperator buf [%ld]: %*s\n", strlen(res_buf),
                      (int)target_size, calc_buf);
          break;
        }
        DEBUG_PRINT("peek successful. popping %c from %*s ...\n", pop_result,
                    (int)target_size, stack);
        pop(stack, &pop_result);
        int stack_priority = get_priority(pop_result);

        if (this_priority < stack_priority) {
          DEBUG_PRINT("caching calc %c ...\n", pop_result);
          push(calc_buf, pop_result);
        } else {
          DEBUG_PRINT("appending %c to result beacuse of higher priority...\n",
                      pop_result);
          push(res_buf, pop_result);
        }
      }
      DEBUG_PRINT("stored calcs [%ld]: %s\n", strlen(calc_buf), calc_buf);
      char pop_result;
      while (pop(calc_buf, &pop_result)) {
        DEBUG_PRINT("pushing back %c to main stack %s \n", pop_result, stack);
        push(stack, pop_result);
      }
      push(stack, *p);
      continue;
    }
    fprintf(stderr, "unrecognized character %c at %ld", *p, p - argv[1]);
    return -2;
  }
  if (*stack)
    push(res_buf, *stack);
  DEBUG_PRINT("result:\t\t");
  printf("%s\n", res_buf);
  free(res_buf);
  free(stack);

  return 0;
}
